1 /** 2 * Common scan line storage implementations 3 * 4 * License: 5 * Copyright Devisualization (Richard Andrew Cattermole) 2014 - 2017. 6 * Distributed under the Boost Software License, Version 1.0. 7 * (See accompanying file LICENSE_1_0.txt or copy at 8 * http://www.boost.org/LICENSE_1_0.txt) 9 */ 10 module devisualization.image.storage.base; 11 import std.experimental.color : isColor; 12 import stdx.allocator : IAllocator, ISharedAllocator, theAllocator, processAllocator; 13 14 /** 15 * A fairly simple image storage type using a horizontal scan line memory order. 16 * 17 * Will automatically deallocate its memory when it goes out of scope. 18 * Should not be copied or moved around. 19 * 20 * See_Also: 21 * ImageStorage 22 */ 23 struct ImageStorageHorizontal(Color) if (isColor!Color) { 24 private { 25 size_t width_, height_; 26 IAllocator allocator; 27 Color[][] data; 28 } 29 30 @disable 31 this(this); 32 33 /// 34 this(size_t width, size_t height, IAllocator allocator = theAllocator()) @trusted { 35 import stdx.allocator : makeArray; 36 this.allocator = allocator; 37 38 width_ = width; 39 height_ = height; 40 41 data = allocator.makeArray!(Color[])(width); 42 43 foreach(_; 0 .. width) { 44 data[_] = allocator.makeArray!Color(height); 45 } 46 } 47 48 /// 49 this(size_t width, size_t height, shared(ISharedAllocator) allocator) @trusted shared { 50 /+import stdx.allocator : makeArray; 51 this.allocator = allocator; 52 53 width_ = width; 54 height_ = height; 55 56 data = allocator.makeArray!(Color[])(width); 57 58 foreach(_; 0 .. width) { 59 data[_] = allocator.makeArray!Color(height); 60 }+/ 61 } 62 63 ~this() @trusted { 64 import stdx.allocator : dispose; 65 66 foreach(_; 0 .. width_) { 67 allocator.dispose(data[_]); 68 } 69 70 allocator.dispose(data); 71 } 72 73 @property { 74 /// 75 size_t width() @nogc nothrow @safe { return width_; } 76 /// 77 size_t width() @nogc nothrow @safe shared { return width_; } 78 79 /// 80 size_t height() @nogc nothrow @safe { return height_; } 81 /// 82 size_t height() @nogc nothrow @safe shared { return height_; } 83 } 84 85 /// 86 Color getPixel(size_t x, size_t y) @nogc @safe { return data[x][y]; } 87 /// 88 Color getPixel(size_t x, size_t y) @nogc @safe shared { return data[x][y]; } 89 90 /// 91 void setPixel(size_t x, size_t y, Color value) @nogc @safe { data[x][y] = value; } 92 /// 93 void setPixel(size_t x, size_t y, Color value) @nogc @safe shared { data[x][y] = value; } 94 95 /// 96 Color opIndex(size_t x, size_t y) @nogc @safe { return getPixel(x, y); } 97 Color opIndex(size_t x, size_t y) @nogc @safe shared { return getPixel(x, y); } 98 99 /// 100 void opIndexAssign(Color value, size_t x, size_t y) @nogc @safe { setPixel(x, y, value); } 101 void opIndexAssign(Color value, size_t x, size_t y) @nogc @safe shared { setPixel(x, y, value); } 102 103 bool resize(size_t newWidth, size_t newHeight) @trusted shared 104 { return (cast()this).resize(newWidth, newHeight); } 105 106 /// 107 bool resize(size_t newWidth, size_t newHeight) @trusted 108 in { 109 assert(newWidth > 0); 110 assert(newHeight > 0); 111 } body { 112 import stdx.allocator : expandArray, shrinkArray, makeArray; 113 114 if (newWidth == width_ && newHeight == height_) return true; 115 size_t deltaHeight; 116 117 if (width_ < newWidth) { 118 assert(allocator.expandArray!(Color[])(data, newWidth - width_)); 119 } else if (width_ == newWidth) { 120 } else { 121 assert(allocator.shrinkArray!(Color[])(data, width_ - newWidth)); 122 } 123 124 if (height_ < newHeight) 125 deltaHeight = newHeight - height_; 126 else 127 deltaHeight = height_ - newHeight; 128 129 foreach(_; 0 .. width_) { 130 if (height_ < newHeight) { 131 assert(allocator.expandArray!Color(data[_], deltaHeight)); 132 } else if (height_ == newHeight) { 133 } else { 134 assert(allocator.shrinkArray!Color(data[_], deltaHeight)); 135 } 136 } 137 138 if (width_ < newWidth) { 139 foreach(_; width_-1 .. newWidth) { 140 auto got = allocator.makeArray!Color(newHeight); 141 assert(got !is null); 142 data[_] = got; 143 } 144 } 145 146 width_ = newWidth; 147 height_ = newHeight; 148 return true; 149 } 150 } 151 152 /// 153 unittest { 154 import std.experimental.color; 155 ImageStorageHorizontal!RGB8 image = ImageStorageHorizontal!RGB8(1, 1); 156 image.resize(2, 2); 157 158 assert(image.width == 2); 159 assert(image.height == 2); 160 assert(image[1, 1] == image[0, 0]); 161 } 162 163 /** 164 * A fairly simple image storage type using a vertical scan line memory order. 165 * 166 * Will automatically deallocate its memory when it goes out of scope. 167 * Should not be copied or moved around. 168 * 169 * See_Also: 170 * ImageStorage 171 */ 172 struct ImageStorageVertical(Color) if (isColor!Color) { 173 private { 174 size_t width_, height_; 175 IAllocator allocator; 176 Color[][] data; 177 } 178 179 /// 180 this(size_t width, size_t height, IAllocator allocator = theAllocator()) @trusted { 181 import stdx.allocator : makeArray; 182 this.allocator = allocator; 183 184 width_ = width; 185 height_ = height; 186 187 data = allocator.makeArray!(Color[])(height); 188 189 foreach(_; 0 .. height) { 190 data[_] = allocator.makeArray!Color(width); 191 } 192 } 193 194 ~this() @trusted { 195 import stdx.allocator : dispose; 196 197 foreach(_; 0 .. width_) { 198 allocator.dispose(data[_]); 199 } 200 201 allocator.dispose(data); 202 } 203 204 @property { 205 /// 206 size_t width() @nogc nothrow @safe { return width_; } 207 208 /// 209 size_t height() @nogc nothrow @safe { return height_; } 210 } 211 212 /// 213 Color getPixel(size_t x, size_t y) @nogc @safe { return data[y][x]; } 214 215 /// 216 void setPixel(size_t x, size_t y, Color value) @nogc @safe{ data[y][x] = value; } 217 218 /// 219 Color opIndex(size_t x, size_t y) @nogc @safe{ return getPixel(x, y); } 220 221 /// 222 void opIndexAssign(Color value, size_t x, size_t y) @nogc @safe { setPixel(x, y, value); } 223 224 /// 225 bool resize(size_t newWidth, size_t newHeight) @trusted 226 in { 227 assert(newWidth > 0); 228 assert(newHeight > 0); 229 } body { 230 import stdx.allocator : expandArray, shrinkArray, makeArray; 231 232 if (newWidth == width_ && newHeight == height_) return true; 233 size_t deltaWidth; 234 235 if (height_ < newHeight) { 236 assert(allocator.expandArray!(Color[])(data, newHeight - height_)); 237 } else if (height_ == newHeight) { 238 } else { 239 assert(allocator.shrinkArray!(Color[])(data, height_ - newHeight)); 240 } 241 242 if (width_ < newWidth) 243 deltaWidth = newWidth - width_; 244 else 245 deltaWidth = width_ - newWidth; 246 247 foreach(_; 0 .. height_) { 248 if (width_ < newWidth) { 249 assert(allocator.expandArray!Color(data[_], deltaWidth)); 250 } else if (width_ == newWidth) { 251 } else { 252 assert(allocator.shrinkArray!Color(data[_], deltaWidth)); 253 } 254 } 255 256 if (height_ < newHeight) { 257 foreach(_; height_-1 .. newHeight) { 258 auto got = allocator.makeArray!Color(newWidth); 259 assert(got !is null); 260 data[_] = got; 261 } 262 } 263 264 width_ = newWidth; 265 height_ = newHeight; 266 return true; 267 } 268 } 269 270 /// 271 unittest { 272 import std.experimental.color; 273 ImageStorageVertical!RGB8 image = ImageStorageVertical!RGB8(1, 1); 274 image.resize(2, 2); 275 276 assert(image.width == 2); 277 assert(image.height == 2); 278 assert(image[1, 1] == image[0, 0]); 279 }